package com.ydj.framework.interceptor;

import com.ydj.framework.annotation.Idempotence;
import com.ydj.framework.exception.CommonException;
import com.ydj.framework.exception.RequestFrequentlyException;
import com.ydj.framework.filter.ExtHttpServletRequest;
import com.ydj.framework.filter.ExtHttpServletResponse;
import com.ydj.framework.redis.DistributedLock;
import com.ydj.framework.redis.RedisDb;
import com.ydj.framework.redis.RedisKey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

/**
 * @author
 * @desc ...
 * @date 2021-01-21 10:49:56
 */
@Slf4j
@Component
public class IdempotenceInterceptor extends BaseInterceptor {

    @Autowired
    RedisDb redisDb;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (!(request instanceof ExtHttpServletRequest) || !(response instanceof ExtHttpServletResponse)) {
            return false;
        }

        // 检测是否允许非登录状态访问
        ExtHttpServletRequest extRequest = (ExtHttpServletRequest) request;
        //获取token
        String requestToken = extRequest.getToken();
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        //请求方法、
        Method method = handlerMethod.getMethod();
        //请求参数
        String body = extRequest.getRequestString();

        Idempotence idempotence = method.getAnnotation(Idempotence.class);
        if (idempotence == null) {
            return true;
        }
        //请求上下文
        extRequest.getInterceptorContext().setHasIdempotenceAnnotation(true);
        String mdKey = RedisKey.getIdempotence(String.valueOf((requestToken + method.getName() + body).hashCode()));

        // 当被锁时候，再次请求，抛出错误。应用场景是：同一个0.001秒内两次连续请求添加活动
        boolean throwExceptionWhenLocking = idempotence.throwExceptionWhenLocking();
        if (throwExceptionWhenLocking) {
            String exceptionMessage = idempotence.throwExceptionWhenLockingMessage();
            Boolean existLock = redisDb.checkLock(mdKey);
            if (existLock == null) {
                CommonException.throwException("连接Rds数据库错误");
            }
            if (existLock) {
                CommonException.throwException(exceptionMessage);
            }
        }

        // 请求完毕后，马上释放锁
        boolean autoRelease = idempotence.autoRelease();
        extRequest.getInterceptorContext().setIsReturn(autoRelease);

        long lockTime = idempotence.lockTime();
        DistributedLock distributedLock;
        if (autoRelease) {
            // 就算业务层面说不释放，其实也是需要释放的。一般接口也不会出现1分钟
            distributedLock = new DistributedLock(mdKey, 60 * 1000L);
        } else {
            distributedLock = new DistributedLock(mdKey, lockTime);
        }
        extRequest.getInterceptorContext().setLock(distributedLock);
        // 加锁
        Boolean successLock = redisDb.distributedLock(extRequest.getInterceptorContext().getLock(), 0, 0);
        if (successLock != null && successLock) {
            return true;
        } else {
            throw new RequestFrequentlyException();
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        ExtHttpServletRequest extHttpServletRequest = (ExtHttpServletRequest) request;
        if (extHttpServletRequest.getInterceptorContext().getHasIdempotenceAnnotation() && extHttpServletRequest.getInterceptorContext().getIsReturn()) {
            //释放幂等性分布式锁
            redisDb.releaseLock(extHttpServletRequest.getInterceptorContext().getLock());
        }
    }
}
